//
//  ZPlayer.swift
//  ZPlayer
//
//  Created by zuber on 2018/11/14.
//  Copyright © 2018年 zuber. All rights reserved.
//

import UIKit
import AVFoundation
import Photos

protocol ZPlayerDelegate: class {
  func zplayerDidClose(_ view: ZPlayer)
}

class ZPlayer: UIView{
  
  struct KeyPath {
    static let loadedTimeRanges = "loadedTimeRanges"
    static let status = "status"
    static let playbackBufferEmpty = "playbackBufferEmpty" // 缓存不足播放
    static let playbackLikelyToKeepUp = "playbackLikelyToKeepUp" // 缓存足够播放
  }
  
  enum `Type` {
    case url(URL)
    case local(URL)
  }
  
  var type: Type? {
    didSet{
      self.setupIfNeeded()
    }
  }
  private var playerItem: AVPlayerItem!
  private var player: AVPlayer!
  private var playerLayer: AVPlayerLayer!
  private var link: CADisplayLink!
  private var coverView: ZCoverView!
  private var activityView: UIActivityIndicatorView!
  private var errorLabel: UILabel!
  
  weak var owner: UIViewController?
  weak var delegate: ZPlayerDelegate?
  
  private var isFinished = false
  private var asset: AVURLAsset!
  private var isFromLocal = false
  
  func setupIfNeeded(){
    guard let type = type , self.playerItem == nil else { return }
    switch type {
    case .url(let url):
      if let url = ZPlayerCache.shared.get(url.absoluteString) {
        asset = AVURLAsset(url: url)
        isFromLocal = true
        print("从本地加载")
      }else{
        asset = AVURLAsset(url: url)
        isFromLocal = false
      }
    case .local(let url):
      isFromLocal = true
      asset = AVURLAsset(url: url)
    }
    let audioSession = AVAudioSession.sharedInstance()
    if #available(iOS 10.0, *) {
      try? audioSession.setCategory(.playback , mode: .default , options: .defaultToSpeaker)
    } else {
    }
    
    playerItem = AVPlayerItem(asset: asset)
    player = AVPlayer(playerItem: self.playerItem)
    playerLayer = AVPlayerLayer(player: player)
    
    layer.insertSublayer(playerLayer, at: 0)
    playerLayer.videoGravity = AVLayerVideoGravity.resizeAspect
    playerLayer.contentsScale = UIScreen.main.scale
    // 监听缓冲进度改变
    playerItem.addObserver(self, forKeyPath: KeyPath.loadedTimeRanges, options: .new, context: nil)
    // 监听状态改变
    playerItem.addObserver(self, forKeyPath: KeyPath.status , options: .new, context: nil)
    playerItem.addObserver(self, forKeyPath: KeyPath.playbackBufferEmpty , options: .new, context: nil)
    playerItem.addObserver(self, forKeyPath: KeyPath.playbackLikelyToKeepUp , options: .new, context: nil)
    
    
    NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidReachEnd(_:)), name: .AVPlayerItemDidPlayToEndTime , object: nil)
    link = CADisplayLink(target: self, selector: #selector(update))
    link.add(to: RunLoop.main, forMode: .default)
    
    // 顶部控制菜单等
    coverView = ZCoverView()
    coverView.delegate = self
    addSubview(coverView)
    if let controller = self.owner {
      coverView.snp.makeConstraints { (make) in
        make.left.right.equalToSuperview()
        if #available(iOS 11, *) {
          make.top.equalTo(controller.view.safeAreaLayoutGuide.snp.top)
          make.bottom.equalTo(controller.view.safeAreaLayoutGuide.snp.bottom)
        }else{
          make.top.bottom.equalToSuperview()
        }
      }
    }else{
      coverView.snp.makeConstraints { (make) in
        make.edges.equalToSuperview()
      }
    }
    activityView = UIActivityIndicatorView()
    addSubview(activityView)
    activityView.snp.makeConstraints { (make) in
      make.center.equalToSuperview()
    }
    activityView.style = .whiteLarge
    activityView.startAnimating()
    
    errorLabel = UILabel()
    addSubview(errorLabel)
    errorLabel.textColor = UIColor.red
    errorLabel.text = "视频加载失败\n点击重新加载"
    errorLabel.font = UIFont.systemFont(ofSize: 15)
    errorLabel.snp.makeConstraints { (make) in
      make.center.equalToSuperview()
    }
    errorLabel.numberOfLines = 2
    errorLabel.isHidden = true
    errorLabel.isUserInteractionEnabled = true
    errorLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(reload)))
  }
  
  override func layoutSubviews() {
    super.layoutSubviews()
    self.playerLayer?.frame = self.bounds
  }
  
  // 是否缓存过
  private var isCached = false
  override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == KeyPath.loadedTimeRanges {
      // 通过监听AVPlayerItem的"loadedTimeRanges"，可以实时知道当前视频的进度缓冲
      let loadedTime = avalableDurationWithplayerItem()
      let totalTime = CMTimeGetSeconds(playerItem.duration)
      let percent = loadedTime/totalTime
      print("当前进度\(percent)")
      if percent >= 1{
        if !isCached {
          isCached = true
          self.cacheItem()
        }
      }
      self.coverView.setLoadedPercent(CGFloat(percent))
    } else if keyPath == KeyPath.status {
      activityView.stopAnimating()
      switch self.playerItem.status {
      case .readyToPlay:
        self.player.play()
        
        self.coverView.setToPlay()
        self.coverView.setToShow()
      case .failed:
        print("失败")
        errorLabel.isHidden = false
      case .unknown:
        print("未知")
        errorLabel.isHidden = false
      }
    }else if keyPath == KeyPath.playbackBufferEmpty {
      activityView.startAnimating()
    }else if keyPath == KeyPath.playbackLikelyToKeepUp {
      activityView.stopAnimating()
    }
  }
  
  @objc
  func reload(){
    releaseResource()
    playerItem = nil
    for view in subviews {
      view.removeFromSuperview()
    }
    setupIfNeeded()
  }
  
  @objc
  func playerItemDidReachEnd(_ notification: Notification) {
    if notification.object as? AVPlayerItem  == player.currentItem {
      isFinished = true
      self.coverView.setToPause()
      self.player.pause()
      self.player.seek(to: .zero)
      self.coverView.setToShow()
    }
  }
  
  private func avalableDurationWithplayerItem() -> TimeInterval {
    guard let loadedTimeRanges = player.currentItem?.loadedTimeRanges ,let first = loadedTimeRanges.first else { return 0 }
    let timeRange = first.timeRangeValue
    let startSeconds = CMTimeGetSeconds(timeRange.start)
    let durationSecound = CMTimeGetSeconds(timeRange.duration)
    let result = startSeconds + durationSecound
    return result
  }
  
  private func formatPlayTime(_ secounds: TimeInterval) -> String {
    if secounds.isNaN {
      return .noneTime
    }
    let Min = Int(secounds / 60)
    let Sec = Int(secounds.truncatingRemainder(dividingBy: 60))
    return String(format: "%02d:%02d", Min, Sec)
  }
  
  @objc
  func update(){
    //暂停的时候
    if !self.coverView.isPlaying{
      return
    }
    if isFinished {
      return
    }
    
    let currentTime = CMTimeGetSeconds(self.player.currentTime())
    let totalTime = TimeInterval(self.playerItem.duration.value) / TimeInterval( self.playerItem.duration.timescale)
    if totalTime.isNaN {
      return
    }
    let percent = CGFloat(currentTime/totalTime)
    if percent >= 1 {
      isFinished = true
    }else{
      isFinished = false
    }
    self.coverView.setPlayedPercent(percent)
    self.coverView.setTimeValue(formatPlayTime(currentTime), totalValue: formatPlayTime(totalTime))
  }
  
  func releaseResource(){
    self.link?.invalidate()
    self.link = nil
    self.coverView.setToOriginal()
    self.player?.currentItem?.cancelPendingSeeks()
    self.player?.currentItem?.asset.cancelLoading()
    playerItem?.removeObserver(self, forKeyPath: KeyPath.loadedTimeRanges)
    playerItem?.removeObserver(self, forKeyPath: KeyPath.status)
    playerItem?.removeObserver(self, forKeyPath: KeyPath.playbackLikelyToKeepUp)
    playerItem?.removeObserver(self, forKeyPath: KeyPath.playbackBufferEmpty)
    NotificationCenter.default.removeObserver(self)
    self.playerLayer?.removeFromSuperlayer()
    self.playerLayer = nil
    self.player = nil
  }
  
  func cacheItem(){
    guard let type = self.type , let asset = asset else { return }
    switch type {
    case .url(let url):
      if !isFromLocal {
        ZPlayerCache.shared.save(asset, forKey: url.absoluteString)
      }
    default:
      break
    }
  }
  
  deinit{
    print("ZPlayer deinit")
  }
}

// MARK: - ZCoverViewDelegate

extension ZPlayer: ZCoverViewDelegate {
  
  func zcoverViewDidClose(_ view: ZCoverView) {
    self.delegate?.zplayerDidClose(self)
  }
  
  func zcoverViewDidPlay(_ view: ZCoverView) {
    switch self.player.status {
    case .readyToPlay:
      if isFinished {
        isFinished = false
      }
      self.player.play()
    default:
      break
    }
  }
  
  func zcoverViewDidPause(_ view: ZCoverView) {
    self.player.pause()
  }
  
  func zcoverView(_ view: ZCoverView, slider: ZSliderView, slideTo percent: CGFloat) {
    switch self.player.status {
    case .readyToPlay:
      if let duration = self.player.currentItem?.duration {
        print(percent)
        print(CGFloat(CMTimeGetSeconds(duration)))
        let duration = percent * CGFloat(CMTimeGetSeconds(duration))
        print(duration)
        print("rount is \(Int64(round(duration)))")
        let seekTime = CMTimeMake(value: Int64(round(duration)), timescale: 1)
        self.player.seek(to: seekTime) { [weak self] (bool) in
          guard let `self` = self else { return }
          slider.isSliding = false
          if !view.isPlaying {
            self.isFinished = false
            view.setToPlay()
            self.player.play()
          }
        }
      }
    default:
      break
    }
  }
}




